#include "Dns.hpp"

#ifndef HOST_NAME_MAX
	#define HOST_NAME_MAX 256
#endif

using namespace std;



template <typename T>
bool Network::Dns::EqualAddr<T>::operator()(const T* a, const T* b)
{
	return (memcmp(a, b, sizeof(T)) < 0);
}



string Network::Dns::getHostName() throw(SocketException)
{
	char buff[HOST_NAME_MAX];
	
	if(gethostname(buff, HOST_NAME_MAX) < 0)
		throw SocketException("Unable to get the host name");
	
	return string(buff);
}



string Network::Dns::getHostName(const IpAddress& ip) throw(SocketException)
{
	char host[NI_MAXHOST];
	const SOCKADDR* addr = ip.sockaddr();
	
	int ret = getnameinfo(addr, ip.sockaddrSize(), host, NI_MAXHOST, NULL, 0, NI_NAMEREQD);
	
	if(ret != 0)
		throw SocketException(errorString(ret));
	
	return string(host);
}



vector<Network::IpAddress> Network::Dns::getHostAddresses(const string& host) throw(SocketException)
{
	set<const in_addr*, EqualAddr<in_addr> > ipv4List;
	set<const in6_addr*, EqualAddr<in6_addr> > ipv6List;
	struct addrinfo hints;
	struct addrinfo* result;
	int ret = 0;
	
	memset(&hints, 0, sizeof(hints));
	hints.ai_family = AF_UNSPEC;// Type d'ip (AF_UNSPEC ou IPv4 ou IPv6)
	hints.ai_socktype = 0;		// Type de socket (SOCK_STREAM ou SOCK_DGRAM ou 0)
	hints.ai_flags = 0;
	hints.ai_protocol = 0;		// Protocole utilisé (ici tous confondu)
	
	ret = getaddrinfo(host.c_str(), NULL, &hints,  &result);
	
	if(ret != 0)
		throw SocketException("Impossible de résoudre l'hôte : " + errorString(ret));
	
	while(result->ai_next != NULL)
	{
	    if(result->ai_family == AF_INET)
	    	ipv4List.insert(&(((SOCKADDR_IN*)result->ai_addr)->sin_addr));
	    else if(result->ai_family == AF_INET6)
	    	ipv6List.insert(&(((SOCKADDR_IN6*)result->ai_addr)->sin6_addr));
	    
	    result = result->ai_next;
	}
	
	vector<IpAddress> addr;
	
	set<const in_addr*, EqualAddr<in_addr> >::iterator ipv4It;
	set<const in6_addr*, EqualAddr<in6_addr> >::iterator ipv6It;
	
	for(ipv4It = ipv4List.begin() ; ipv4It != ipv4List.end() ; ipv4It++)
	{
		SOCKADDR_IN sockaddr;
		memset((void*)&sockaddr, 0, sizeof(sockaddr));
		sockaddr.sin_family = AF_INET;
		sockaddr.sin_addr = **ipv4It;
		addr.push_back(IpAddress((SOCKADDR*)&sockaddr));
	}
	
	for(ipv6It = ipv6List.begin() ; ipv6It != ipv6List.end() ; ipv6It++)
	{
		SOCKADDR_IN6 sockaddr;
		memset((void*)&sockaddr, 0, sizeof(sockaddr));
		sockaddr.sin6_family = AF_INET6;
		sockaddr.sin6_addr = **ipv6It;
		addr.push_back(IpAddress((SOCKADDR*)&sockaddr));
	}
	
	freeaddrinfo(result);
	
	return addr;
}



string Network::Dns::errorString(int errVal)
{
   #ifdef _WIN32
        return string(gai_strerrorA(errVal));
    #else
        return string(gai_strerror(errVal));
    #endif
}




